feat(workspace): offer to install the engine a bound workspace needs - #1169
feat(workspace): offer to install the engine a bound workspace needs#1169ralphstodomingo wants to merge 10 commits into
Conversation
|
Important Draft PR not reviewedDraft PRs are not automatically reviewed by default.
To automatically review draft PRs, update your CodeRabbit configuration: reviews:
auto_review:
drafts: trueThanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Review logStacked on #1168 → #1167 — #1167's review-log comment carries the attach contract; this PR is reviewed the same way, with capped rounds. Claims (in addition to the stack's)
Disclosed residuals
End-to-end rows (from #1158, re-run on this stack before this leaves draft)
Rounds(none yet) Codex rounds
|
|
@codex review against the numbered claims and the disclosed residuals in the review-log comment on this PR: report only a reproducible trace that violates a numbered claim; an instance of a listed residual is disclosed behaviour, not a finding. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 58a3f4ca63
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review against the numbered claims and the disclosed residuals in the review-log comment on this PR: report only a reproducible trace that violates a numbered claim; an instance of a listed residual is disclosed behaviour, not a finding. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9bc01a5d2a
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review against the numbered claims and the disclosed residuals in the review-log comment on this PR: report only a reproducible trace that violates a numbered claim; an instance of a listed residual is disclosed behaviour, not a finding. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 63ba0a1f93
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
afa3fda to
65b2916
Compare
|
@codex review against the numbered claims and the disclosed residuals in the review-log comment on this PR: report only a reproducible trace that violates a numbered claim; an instance of a listed residual is disclosed behaviour, not a finding. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 65b2916f2a
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review against the numbered claims and the disclosed residuals in the review-log comment on this PR: report only a reproducible trace that violates a numbered claim; an instance of a listed residual is disclosed behaviour, not a finding. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a5f7cd1685
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| // engine appeared, or the project is no longer bound. Say nothing. | ||
| if (!offer) return release() | ||
| const latchScope = await currentLatchScope() | ||
| if (isEngineSkipActive(api, offer.workspaceId, latchScope, Date.now())) { |
There was a problem hiding this comment.
Wait for KV hydration before checking the skip latch
Repro: choose Not now, restart the TUI, delay the persisted KV read (for example, hold the tui-kv Flock), and immediately send a message that raises the engine offer. KVProvider initially exposes an empty store with api.kv.ready === false, but this check treats the missing record as an expired latch and opens the dialog; later hydration does not re-check or close it. Thus a persisted Not-now choice can be ignored well within seven days, violating the numbered suppression claim.
Useful? React with 👍 / 👎.
|
@codex review against the numbered claims and the disclosed residuals in the review-log comment on this PR: report only a reproducible trace that violates a numbered claim; an instance of a listed residual is disclosed behaviour, not a finding. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 464ef949da
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review against the numbered claims and the disclosed residuals in the review-log comment on this PR: report only a reproducible trace that violates a numbered claim; an instance of a listed residual is disclosed behaviour, not a finding. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 26831a2ab8
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review against the numbered claims and the disclosed residuals in the review-log comment on this PR: report only a reproducible trace that violates a numbered claim; an instance of a listed residual is disclosed behaviour, not a finding. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2f6fd27db3
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if (!(await awaitKvReady(api.kv))) { | ||
| log.warn("kv store not hydrated in time; checking the engine install latch against what is loaded") | ||
| } |
There was a problem hiding this comment.
Suppress the offer when KV hydration times out
Repro: choose Not now, restart the TUI, keep the tui-kv Flock held for more than three seconds, and immediately send a message. Fresh evidence after the prior fix is that awaitKvReady() now returns false after a fixed timeout, but this branch merely logs and then consults the still-empty store, so the dialog opens within the seven-day suppression period; later hydration does not revisit or close it. On timeout, release/drop this offer and allow a later raise after KV becomes ready instead of treating unhydrated state as an absent latch.
Useful? React with 👍 / 👎.
893ff8f to
b8daef2
Compare
A bound workspace whose declared tools need the local engine used to report a missing engine as a toast with a command in it. This replaces that with an offer: Install now / Copy command / Not now. The install only ever runs from an explicit choice; the turn boundary picks the installed engine up on the next message. The offer crosses to the TUI over the event bus and is re-derived there; headless prints one stderr line.
…now latch `announceRefusal` deduped the offer once per session per verdict, so a session open for longer than the 7-day "Not now" latch never saw the offer again — the TUI's latch check was unreachable. The offer route's dedupe now expires on the same TTL, `OFFER_SKIP_TTL_MS`, defined once in `engine-offer` and shared with the plugin's latch. Toast-only refusals keep "once per session".
The too-old probe memo was honoured for 30s by path alone, so an engine updated in place by the offer's `npm i -g` stayed refused on the next message — and nothing could invalidate the memo, since the offer runs in another module realm. The memo now also carries the binary's fingerprint (size + mtime, symlinks followed) and ends when it changes; an un-stat-able binary falls back to the TTL.
…hed-run line to its session The attach-side dedupe expired seven days after the offer was raised, but the TUI latch runs from "Not now", which can come later — a long-lived session could wait a second full window. Past the window the offer is now re-raised every `OFFER_RECHECK_MS` and the TUI suppresses it until its latch ends. The offer command now carries the session it was raised for, so an attached headless run prints only its own offer, not another session's in the directory.
…ession A headless `run` whose agent uses the task tool creates a child session, and that session's first catalog settled the same verdict and printed the same stderr line again. The headless line is now deduplicated per verdict across the process; sessions still track their own announcement for the toast and offer surfaces.
…ow latch The store is empty until kv.json has been read, so an offer raised on the first message after a restart read a persisted "Not now" as absent and showed the dialog inside the seven days. `api.kv.ready` is a plain getter, so the offer polls it (25 ms, bounded at 3 s) and on timeout proceeds as before.
… tree `Process.run` settles on `close`, and the abort only signalled npm's own pid, so a descendant that kept the stderr pipe open held `installEngine` past the five minutes and the dialog stayed on "Installing…". The install now runs in its own process group, settles on the child's `exit`, and at the deadline signals the group — SIGTERM, then SIGKILL after a grace — before reporting the timeout.
npm, the group leader, normally dies on the deadline's SIGTERM, and its exit cleared the escalation timer — so a descendant that ignored SIGTERM outlived the reported timeout. Once the deadline has fired the SIGKILL to the process group stays scheduled; the group outlives its leader while any member is alive.
2f6fd27 to
b645742
Compare
The dialog already skipped filtering (`skipFilter`); the box itself only collected stray keystrokes while the install ran. `renderFilter` is passed through the plugin dialog API to the component that already supports it.
Issue for this PR
Closes #1157
Type of change
What does this PR do?
Stacked on #1168 (which stacks on #1167) — this PR is the commit on top. It is the install offer from #1158 restacked onto the overlay attach; the dialog and its rules are unchanged, the refusal it substitutes is now the overlay's.
A bound workspace whose declared tools need the local engine used to report a missing engine as a 10-second toast with a command in it. This replaces that with an offer: Install now / Copy command / Not now. The install only ever runs from an explicit choice — the attach never installs anything on its own.
How the offer reaches the TUI: the plugin runtime loads plugins in a separate module realm, so nothing in-process can hand it the offer. It is published on the event bus, which is what toasts already use, and since that event carries no payload the plugin re-derives the detail from the binding on disk and the engine on PATH (
describeOffer, state-free by design). After a successful install the plugin does not reconcile anything itself — it cannot reach the server's module — and does not need to: the turn boundary looks for a missing engine on PATH again every turn, so the engine attaches on the next message.Deliberate details, carried over from the original:
How did you verify your code works?
bun run typecheckclean. New offer suite (routing to surface / bus / toast / headless line, too-old with the found version, healthy engine never offered,describeOfferre-derivation, stderr-not-stdout, install deadline via abort signal, install success verified not assumed) plus the plugin suite; 357 tests pass across the workspace, plugin, precedence and MCP lifecycle suites on this stack.End-to-end rows from #1158 (dialog with all three options and a live declared count; Install now into an isolated prefix with tools on the next message in the same session; Not now suppresses and returns after the latch ages; no Node hides Install now; headless prints exactly one line) are re-run on this stack and recorded in the review-log comment below before this leaves draft.
Not verified here: the successful clipboard path (this host has no clipboard backend); Windows (
npm.cmdfollows existing precedent in this repo).Screenshots / recordings
Terminal UI; captured pane output will accompany the E2E rows on the review-log comment.
Checklist
Summary by cubic
Replaces the toast that printed an install command for a workspace with a missing or too-old engine with an interactive offer: Install now / Copy command / Not now. The install runs only from an explicit choice, and the installed engine attaches on the next message without a restart.
Offer behavior
Written for commit 089bb62. Summary will update on new commits.